package de.yvert.jingle.help;

import java.nio.ByteOrder;

/**
 * A buffer for byte data. Kind of like <code>DataInput</code> crossed with
 * <code>ByteArrayInputStream</code> and <code>java.nio.ByteOrder</code>.
 * 
 * @author Ulf Ochsenfahrt
 */
public final class Buffer
{

public static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN;
public static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN;

protected ByteOrder order = BIG_ENDIAN;
protected final byte[] data;

public Buffer(ByteOrder order, byte[] data)
{
	this.order = order;
	this.data = data;
}

@Deprecated
public Buffer(byte[] data)
{ this(BIG_ENDIAN, data); }

public int length()
{ return data.length; }

public byte[] toByteArray()
{
	byte[] temp = new byte[data.length];
	System.arraycopy(data, 0, temp, 0, data.length);
	return temp;
}

public void setByteOrder(ByteOrder order)
{ this.order = order; }

public ByteOrder getByteOrder()
{ return order; }

private int get(int pos)
{ return data[pos] & 0xff; }

public boolean readBoolean(int pos)
{ return get(pos) != 0; }

public byte readByte(int pos)
{ return (byte) get(pos); }

public double readDouble(int pos)
{ return Double.longBitsToDouble(readLong(pos)); }

public float readFloat(int pos)
{ return Float.intBitsToFloat(readInt(pos)); }

public float readHalf(int pos)
{ return Half.shortBitsToHalf(readUnsignedShort(pos)); }

public short readShort(int pos)
{ return (short) readUnsignedShort(pos); }

public int readInt(int pos)
{ return (int) readUnsignedInt(pos); }

public long readLong(int pos)
{
	long a = readUnsignedInt(pos);
	long b = readUnsignedInt(pos+4);
	if (order == BIG_ENDIAN)
		return (a << 32) | b;
	else
		return (b << 32) | a;
}

public int readUnsignedByte(int pos)
{ return get(pos); }

public int readUnsignedShort(int pos)
{
	int a = get(pos);
	int b = get(pos+1);
	if (order == BIG_ENDIAN)
		return (a << 8) | b;
	else
		return (b << 8) | a;
}

public long readUnsignedInt(int pos)
{
	int a = get(pos);
	int b = get(pos+1);
	int c = get(pos+2);
	int d = get(pos+3);
	if (order == BIG_ENDIAN)
		return (a << 24) | (b << 16) | (c << 8) | d;
	else
		return (d << 24) | (c << 16) | (b << 8) | a;
}


public void setHalf(int pos, float value)
{ setShort(pos, Half.halfToShortBits(value)); }

public void setFloat(int pos, float value)
{ setInt(pos, Float.floatToIntBits(value)); }

private void set(int pos, byte value)
{ data[pos] = value; }

public void setByte(int pos, byte value)
{ set(pos, value); }

public void setShort(int pos, short value)
{
	if (order == BIG_ENDIAN)
	{
		set(pos+0, (byte) (value >> 8));
		set(pos+1, (byte) value);
	}
	else
	{
		set(pos+1, (byte) (value >> 8));
		set(pos+0, (byte) value);
	}
}

public void setInt(int pos, int value)
{
	if (order == BIG_ENDIAN)
	{
		set(pos+0, (byte) (value >> 24));
		set(pos+1, (byte) (value >> 16));
		set(pos+2, (byte) (value >> 8));
		set(pos+3, (byte) value);
	}
	else
	{
		set(pos+3, (byte) (value >> 24));
		set(pos+2, (byte) (value >> 16));
		set(pos+1, (byte) (value >> 8));
		set(pos+0, (byte) value);
	}
}

}
